#!/usr/bin/env python
"""
Feature name translator for feature names, backed by a list-based data structure
 with stream input

 For implementation details, see below.

Author: Tom Robinson <trobinson@systemsbiology.org>
Initial Revision: February 17th, 2012

"""

## Imports

import re


## Credit documentation

__author__ = "Tom Robinson"
__copyright__ = "Institute for Systems Biology, 2011"
__credits__ = ["Tom Robinson"]
__license__ = "Apache 2.0"
__version__ = "1.0"
__maintainer__ = "Tom Robinson"
__email__ = "trobinson@systemsbiology.org"
__status__ = "Production"
__platform__ = "Multi-OS, Python 2.6+"


## Globals

MAX_LINE=40960                  ## Maximum line length (in characters)


## Classes

class FeatureTranslator(object):
    """ Feature translator: simple tool to match or return the set of candidate
     regular expressions available for a given feature name, in input order.

    """

    __logger = None     ## The logger paired to this object ##
    __translators = []      ## The translators list used ##

    def __init__(self,logger=None):
        """ Create a new instance of this class

        Parameters
        ----------
        logger : logger
            Optional logger for verbose informational messages

        Return Value
        ------------
        FeatureTranslator
            A new instance of this class, configured with the given params

        Examples
        --------
        ft = FeatureTranslator()    # A new instance is created with default params 

        """
        self.__logger = logger

    def match(self,featureName):
        """ Look up the file in our backing list of regular expressions,
         returning a boolean stating if it was present or not.

        Parameters
        ----------
        featureName : string
            Name of the feature to attempt regexp matching upon

        Return Value
        ------------
        bool
            True if this filename matched one of our regular expressions; 
            False otherwise

        Examples
        --------
        ft.match('N:CLIN::MyFeature:FromClin')  # Attempt to match this feature

        """

        for translation in self.__translators:
            if re.match(translation[0],featureName):
                return True
        return False

    def translations(self,featureName):
        """ Return all candidate translations for this feature name in our
         backing data structure

        Parameters
        ----------
        featureName : string
            Name of the feature to attempt regexp matching upon

        Return Value
        ------------
        bool
            True if this filename matched one of our regular expressions; 
            False otherwise

        Examples
        --------
        ft.translations('N:CLIN::MyFeature:FromClin') 
                                               # Translations are returned

        """

        ret = []
        for translation in self.__translators:
            if re.match(translation[0],featureName):
                ret.append(translation[1])
        return ret

    def appendFromFile(self, filepath):
        """ Add the contents of a translator file to our internal dictionary

        Parameters
        ----------
        filepath : string
            Path to the target file to open for reading

        Return Value
        ------------
        None (contents are appended if an exception isn't raised)

        Examples
        --------
        ft.appendFromFile('/path/to/my/translator.txt')

        """ 
        if self.__logger: 
            self.__logger.info('appendFromFile: attempting to read %s' % \
                filepath) 

        fhandle = open(filepath,'rU')
        self.appendFromStream(fhandle)
        fhandle.close()
        
        if self.__logger: 
            self.__logger.info('appendFromFile: finished reading %s' % \
                filepath) 


    def appendFromStream(self, stream, separator='\t', verbatim=False, lines=-1):
        """ Add the contents of an translator stream to the internal dictionaries 

        Parameters
        ----------
        stream : streamlike
            An open, readable stream object

        separator : string
            Separator for the given input translation table. Default: '\t'

        verbatim : bool
            If True, keep the input regular expression in raw form. Otherwise,
            perform sparse reprocessing to ensure certain forms of correctness.

        lines : int
            Number of lines to read from the stream. Default: -1 (disable)

        Return Value
        ------------
        None (contents are appended if an exception isn't raised)

        Examples
        --------
        ft.appendFromStream(mystream)

        """ 
        if not isinstance(lines,int) and lines < -1:
            raise ValueError("Number of lines specified was invalid: should be"
                + " positive, or -1 to disable.")

        line = '\n'
        lineCount = 0
        while line:

            # Honor the number of lines if this feature is enabled
            if lines != -1:
                if lines == 0:
                    break
                lines -= 1

            # Read a line; increment the line count
            line = stream.readline(MAX_LINE)
            if not line:
                break
            if len(line) == MAX_LINE:
                raise IOError('Line length was too large (%s).' \
                  % str(MAX_LINE) \
                  + 'Please ensure the file is being parsed correctly.')
       
            lineCount += 1         
            
            if verbatim:
                line = line.strip('\r\n')
            else:
                line = line.strip()    # Note: removes trailing whitespace from
                                       #  any expression that expects it
            
            split = line.split(separator)
            feature = line
            translation = None
            if len(split) == 2:
                feature = split[0]
                translation = split[1]
            elif len(split) > 2:
                raise ValueError("Incorrect column length (%d) for line: %s" % \
                                   (len(split), line))

            if feature and feature[0] != "#":
                if verbatim:
                    self.__translators.append([line.rstrip()])
                else:
                    self.__translators.append( \
                      ["^" + line.rstrip().lstrip("^").rstrip("$") + "$"])

